import React, { useRef, useEffect } from 'react';
import './index.less';
import Vec4 from './Vec4';
import Mat4 from './Mat4';
const VIEW_SIZE = 400;
function App() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  useEffect(() => {
    const elCanvas = canvasRef.current;
    if (!elCanvas) {
      return;
    }
    elCanvas.setAttribute('width', VIEW_SIZE + 'px');
    elCanvas.setAttribute('height', VIEW_SIZE + 'px');
    const ctx = elCanvas.getContext('2d');
    if (!ctx) {
      return;
    }
    // 正方体模型8个顶点，后平面左下角在原点
    const createCube = (
      size: number // 大小
    ) => {
      return [
        // 前平面 上右下左
        new Vec4(0, size, size),
        new Vec4(size, size, size),
        new Vec4(size, 0, size),
        new Vec4(0, 0, size),

        // 后平面 上右下左
        new Vec4(0, size, 0),
        new Vec4(size, size, 0),
        new Vec4(size, 0, 0),
        new Vec4(0, 0, 0),
      ];
    };
    const cubeVertexes = createCube(50);
    // 矩形顶点的连线
    const lines = [
      [0, 1],
      [1, 2],
      [2, 3],
      [3, 0],

      [4, 5],
      [5, 6],
      [6, 7],
      [7, 4],

      [0, 4],
      [1, 5],
      [2, 6],
      [3, 7],
    ];

    // 创建一个正交投影矩阵 https://www.bilibili.com/video/BV1X7411F744?p=4 P4 49:00
    const createOrthoMath = ({
      l,
      r,
      t,
      b,
      f,
      n,
    }: {
      l: number; // left
      r: number; // right
      t: number; // top
      b: number; // bootom
      f: number; // far
      n: number; // near
    }) => {
      return new Mat4([
        new Vec4(2 / (r - l), 0, 0, -(r + l) / 2),
        new Vec4(0, 2 / (t - b), 0, -(t + b) / 2),
        new Vec4(0, 0, 2 / (n - f), -(n + f) / 2),
        new Vec4(0, 0, 0, 1),
      ]);
    };
    const matProjectionOrtho = createOrthoMath({
      l: -VIEW_SIZE / 2,
      r: VIEW_SIZE / 2,
      t: VIEW_SIZE / 2,
      b: -VIEW_SIZE / 2,
      f: -VIEW_SIZE / 2,
      n: VIEW_SIZE / 2,
    });

    // 创建一个透视投影矩阵
    const createPerspMath = ({
      fovy,
      aspect,
      near,
      far,
    }: {
      fovy: number;
      aspect: number;
      near: number;
      far: number;
    }) => {
      const n = Math.abs(near);
      const t = Math.tan(fovy / 2) * n;
      const orth = createOrthoMath({
        l: -t,
        r: t,
        t: t,
        b: -t,
        n: near,
        f: far,
      });
      return orth.multiply(
        new Mat4([
          new Vec4(near, 0, 0, 0),
          new Vec4(0, near, 0, 0),
          new Vec4(0, 0, near + far, -near * far),
          new Vec4(0, 0, 1, 0),
        ])
      );
    };
    const matProjectionPersp = createPerspMath({
      fovy: 45,
      aspect: 1,
      near: VIEW_SIZE / 2,
      far: -VIEW_SIZE / 2,
    });

    // 动画循环
    let angle = 45;
    const draw = () => {
      ctx.clearRect(0, 0, VIEW_SIZE, VIEW_SIZE);

      angle = (angle + 0.01) % 360;

      // 正交投影
      const mOrtho = matProjectionOrtho
        .xRotation(angle)
        .yRotation(angle)
        // .zRotation(angle)
        // .scale(new Vec4(0.005, 0.005, 0.005))
        .translate(new Vec4(-25, -25, -25));

      const cubeProjectionOrtho = cubeVertexes.map((vertex) => {
        return mOrtho.multiplyVec(vertex);
      });
      lines.forEach((line) => {
        const startP = cubeProjectionOrtho[line[0]];
        const endP = cubeProjectionOrtho[line[1]];
        ctx.beginPath();
        ctx.moveTo(
          startP.x * VIEW_SIZE + VIEW_SIZE / 2,
          startP.y * VIEW_SIZE + VIEW_SIZE / 2
        );
        ctx.lineTo(
          endP.x * VIEW_SIZE + VIEW_SIZE / 2,
          endP.y * VIEW_SIZE + VIEW_SIZE / 2
        );
        ctx.stroke();
        ctx.closePath();
      });

      // 透视投影
      const mPersp = matProjectionPersp
        .xRotation(angle)
        .yRotation(angle)
        // .zRotation(angle)
        .scale(new Vec4(0.005, 0.005, 0.005))
        .translate(new Vec4(-25, -25, -25));
      const cubeProjectionPersp = cubeVertexes.map((vertex) => {
        return mPersp.multiplyVec(vertex);
      });
      lines.forEach((line) => {
        const startP = cubeProjectionPersp[line[0]];
        const endP = cubeProjectionPersp[line[1]];
        ctx.beginPath();
        ctx.moveTo(
          startP.x * VIEW_SIZE + VIEW_SIZE / 2,
          startP.y * VIEW_SIZE + VIEW_SIZE / 2
        );
        ctx.lineTo(
          endP.x * VIEW_SIZE + VIEW_SIZE / 2,
          endP.y * VIEW_SIZE + VIEW_SIZE / 2
        );
        ctx.stroke();
        ctx.closePath();
      });
      ctx.fillRect(VIEW_SIZE / 2, VIEW_SIZE / 2, 1, 1);

      requestAnimationFrame(draw);
    };
    draw();
  });
  return (
    <div className='page-cube'>
      <canvas ref={canvasRef} />
    </div>
  );
}

export default App;
